package com.gosling.gosling.sooyoo;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.HashMap;
import java.util.Map;

/**
 * 等额本息计算工具类
 *
 * <p>
 * 等额本息还款，也称定期付息，即借款人每月按相等的金额偿还贷款本息，其中每月贷款利息按月初剩余贷款本金计算并逐月结清。把按揭贷款的本金总额与利息总额相加，
 * 然后平均分摊到还款期限的每个月中。作为还款人，每个月还给银行固定金额，但每月还款额中的本金比重逐月递增、利息比重逐月递减。
 */
public class SooYooAverageLoanPlanTest {

   private static final BigDecimal NUM_12 = new BigDecimal("12");

   /**
    * 每月偿还本金和利息
    * <p>
    * 公式：每月偿还本息=〔贷款本金×月利率×(1＋月利率)＾还款月数〕÷〔(1＋月利率)＾还款月数-1〕
    *
    * @param invest     总借款额（贷款本金,单位分）
    * @param totalMonth 还款总月数
    * @return 每月偿还本金和利息(入1 单位分)
    */
   public static BigDecimal getPerMonthPrincipalInterest(BigDecimal invest, int totalMonth, BigDecimal monthRate) {
      BigDecimal onePlusRate = BigDecimal.ONE.add(monthRate);
      double a = onePlusRate.doubleValue();
      double pow = Math.pow(a, totalMonth);
      BigDecimal v = invest.multiply((monthRate.multiply(BigDecimal.valueOf(pow))));
      BigDecimal z = BigDecimal.valueOf(Math.pow(BigDecimal.ONE.add(monthRate).doubleValue(), totalMonth) - 1);
      BigDecimal perMonthPrincipalInterest = v.divide(z, 6, RoundingMode.HALF_DOWN);
      return perMonthPrincipalInterest;
   }

   /**
    * 等额本息的每月偿还利息
    * <p>
    * 公式：每月偿还利息=贷款本金×月利率×〔(1+月利率)^还款月数-(1+月利率)^(还款月序号-1)〕÷〔(1+月利率)^还款月数-1〕
    *
    * @param invest     总借款额（贷款本金,分）
    * @param totalMonth 还款总月数
    * @return 每月偿还利息(入1 单位分)
    */
   public static Map<Integer, BigDecimal> getPerMonthInterest(BigDecimal invest, int totalMonth, BigDecimal monthRate) {
      Map<Integer, BigDecimal> map = new HashMap<>();
      //1+月利率
      BigDecimal onePlusRate = BigDecimal.ONE.add(monthRate);
      double a = onePlusRate.doubleValue();
      BigDecimal monthInterest;
      for (int i = 1; i < totalMonth + 1; i++) {
         BigDecimal multiply = invest.multiply(monthRate);
         double sub = Math.pow(a, totalMonth) - Math.pow(a, i - 1);
         monthInterest = multiply.multiply(new BigDecimal(sub)).divide(new BigDecimal(Math.pow(a, totalMonth) - 1), 6, RoundingMode.HALF_DOWN);
         map.put(i, monthInterest);
      }
      return map;
   }

   /**
    * 等额本息的每月偿还本金（月还本息-月还利息）
    *
    * @param invest     总借款额（贷款本金,分）
    * @param totalMonth 还款总月数
    * @return 每月偿还本金(取整舍 单位分)
    */
   public static Map<Integer, BigDecimal> getPerMonthPrincipal(BigDecimal invest, int totalMonth, BigDecimal monthRate) {
      //1 + monthRate
      BigDecimal onePlusRate = BigDecimal.ONE.add(monthRate);
      double a = onePlusRate.doubleValue();
      double pow = Math.pow(a, totalMonth);
      BigDecimal monthIncome = invest.multiply(monthRate).multiply(new BigDecimal(pow).divide(new BigDecimal(pow - 1), 6, RoundingMode.HALF_DOWN));
      Map<Integer, BigDecimal> mapPrincipal = new HashMap<>();
      BigDecimal monthInterest;
      for (int i = 1; i < totalMonth + 1; i++) {
         BigDecimal multiply = invest.multiply(monthRate);
         double sub = pow - (Math.pow(a, i - 1));
         monthInterest = multiply.multiply(new BigDecimal(sub)).divide(new BigDecimal(pow - 1), 6, RoundingMode.HALF_DOWN);
         mapPrincipal.put(i, monthIncome.subtract(monthInterest));
      }
      return mapPrincipal;
   }

   /**
    * 等额本息的总利息
    *
    * @param invest     总借款额（贷款本金）
    * @param totalMonth 还款总月数
    * @return 总利息 (单位分)
    */
   public static BigDecimal getInterestCount(BigDecimal invest, int totalMonth, BigDecimal monthRate) {
      BigDecimal count = new BigDecimal("0");
      Map<Integer, BigDecimal> mapInterest = getPerMonthInterest(invest, totalMonth, monthRate);
      for (Map.Entry<Integer, BigDecimal> entry : mapInterest.entrySet()) {
         count = count.add(entry.getValue());
      }
      return count;
   }

   public static void main(String[] args) {
      int month = 12;
      BigDecimal invest = new BigDecimal("10000"); // 本金
      BigDecimal yearRate = new BigDecimal("0.1"); // 年利率
      //月利率 = 年利率 / 12
      BigDecimal monthRate = yearRate.divide(NUM_12, 6, RoundingMode.HALF_DOWN);
      BigDecimal perMonthPrincipalInterest = getPerMonthPrincipalInterest(invest, month, monthRate);
      System.out.println("等额本息---每月还款本息：" + perMonthPrincipalInterest);
      Map<Integer, BigDecimal> mapInterest = getPerMonthInterest(invest, month, monthRate);
      System.out.println("等额本息---每月还款利息：" + mapInterest);
      Map<Integer, BigDecimal> mapPrincipal = getPerMonthPrincipal(invest, month, monthRate);
      System.out.println("等额本息---每月还款本金：" + mapPrincipal);
      BigDecimal count = getInterestCount(invest, month, monthRate);
      System.out.println("等额本息---总利息：" + count);
      for (int i = 1; i < month + 1; i++) {
         if (i < month) {
            System.out.println("等额本息---" + i + "月还款本金:" + mapPrincipal.get(i) + ",还款利息:" + mapInterest.get(i));
            invest = invest.subtract(mapPrincipal.get(i));
         } else {
            System.out.println("等额本息---" + i + "月还款本金:" + invest + ",还款利息:" + (perMonthPrincipalInterest.divide(invest, 6, RoundingMode.HALF_DOWN)));
         }
      }
   }
}  
